"""
注意!
使用本模块，需要先安装所在操作系统对应的chrome和chrome驱动
"""

from pickle import NONE
import re
from urllib import request
from fastapi import Cookie
import requests
from typing import Optional, Tuple, Dict, List
import time
from urllib.parse import urlparse
import config
import logging
import threading
import atexit
import psutil
import os
import tempfile
import shutil
import weakref
from contextlib import contextmanager
import gc
from common.constants import (
    WEBDRIVER_POOL_MAX_SIZE,
    WEBDRIVER_MAX_IDLE_TIME,
    WEBDRIVER_MAX_REQUEST_COUNT,
    WEBDRIVER_MAX_CONSECUTIVE_FAILURES,
    WEBDRIVER_MEMORY_WARNING_THRESHOLD,
    WEBDRIVER_MEMORY_CRITICAL_THRESHOLD,
    WEBDRIVER_CLEANUP_INTERVAL,
    WEBDRIVER_DEEP_CLEANUP_INTERVAL,
    CHROME_PROCESS_TIMEOUT
)

logger = logging.getLogger("douyin_parser")

# 资源跟踪器，用于管理临时文件和目录
class ResourceTracker:
    """统一管理所有临时资源，确保程序退出时清理"""
    _instance = None
    _lock = threading.Lock()

    def __new__(cls):
        if cls._instance is None:
            with cls._lock:
                if cls._instance is None:
                    cls._instance = super().__new__(cls)
                    cls._instance._initialized = False
        return cls._instance

    def __init__(self):
        if self._initialized:
            return
        self._initialized = True
        self._temp_dirs: List[str] = []
        self._temp_files: List[str] = []
        self._lock = threading.Lock()
        logger.info("资源跟踪器已初始化")

    def register_temp_dir(self, path: str):
        """注册临时目录"""
        with self._lock:
            if path not in self._temp_dirs:
                self._temp_dirs.append(path)
                logger.debug(f"注册临时目录: {path}")

    def register_temp_file(self, path: str):
        """注册临时文件"""
        with self._lock:
            if path not in self._temp_files:
                self._temp_files.append(path)
                logger.debug(f"注册临时文件: {path}")

    def cleanup_all(self):
        """清理所有注册的资源"""
        with self._lock:
            logger.info(f"开始清理 {len(self._temp_dirs)} 个临时目录和 {len(self._temp_files)} 个临时文件")

            # 清理临时目录
            for temp_dir in self._temp_dirs[:]:
                try:
                    if os.path.exists(temp_dir):
                        shutil.rmtree(temp_dir)
                        logger.info(f"清理临时目录: {temp_dir}")
                except Exception as e:
                    logger.warning(f"清理临时目录失败 {temp_dir}: {e}")
                finally:
                    if temp_dir in self._temp_dirs:
                        self._temp_dirs.remove(temp_dir)

            # 清理临时文件
            for temp_file in self._temp_files[:]:
                try:
                    if os.path.exists(temp_file):
                        os.remove(temp_file)
                        logger.info(f"清理临时文件: {temp_file}")
                except Exception as e:
                    logger.warning(f"清理临时文件失败 {temp_file}: {e}")
                finally:
                    if temp_file in self._temp_files:
                        self._temp_files.remove(temp_file)

            logger.info("资源清理完成")

    def get_temp_dir(self, prefix: str = 'chrome_profile_') -> str:
        """创建并注册临时目录"""
        temp_dir = tempfile.mkdtemp(prefix=prefix)
        self.register_temp_dir(temp_dir)
        return temp_dir


# WebDriver信息封装类
class WebDriverInfo:
    """封装WebDriver及其元信息"""
    def __init__(self, driver, user_data_dir: str):
        self.driver = driver
        self.user_data_dir = user_data_dir
        self.last_used = time.time()
        self.created_at = time.time()
        self.request_count = 0
        self.consecutive_failures = 0
        self._lock = threading.Lock()

    def update_usage(self):
        """更新使用统计"""
        with self._lock:
            self.last_used = time.time()
            self.request_count += 1

    def increment_failures(self):
        """增加连续失败次数"""
        with self._lock:
            self.consecutive_failures += 1

    def reset_failures(self):
        """重置失败计数"""
        with self._lock:
            self.consecutive_failures = 0


# 优化的WebDriver连接池管理
class WebDriverPool:
    """优化的WebDriver连接池，提供更好的资源管理和内存泄漏防护"""

    def __init__(
        self,
        max_size: int = WEBDRIVER_POOL_MAX_SIZE,
        max_idle_time: int = WEBDRIVER_MAX_IDLE_TIME,
        max_request_count: int = WEBDRIVER_MAX_REQUEST_COUNT,
        max_consecutive_failures: int = WEBDRIVER_MAX_CONSECUTIVE_FAILURES,
        memory_warning_threshold: int = WEBDRIVER_MEMORY_WARNING_THRESHOLD,
        memory_critical_threshold: int = WEBDRIVER_MEMORY_CRITICAL_THRESHOLD
    ):
        self.max_size = max_size
        self.max_idle_time = max_idle_time
        self.max_request_count = max_request_count
        self.max_consecutive_failures = max_consecutive_failures
        self.memory_warning_threshold = memory_warning_threshold  # MB
        self.memory_critical_threshold = memory_critical_threshold  # MB

        self._pool: List[WebDriverInfo] = []
        self._lock = threading.RLock()  # 使用可重入锁
        self._cleanup_thread = None
        self._resource_tracker = ResourceTracker()
        self._shutdown = False
        self._start_cleanup_thread()
        logger.info(f"WebDriverPool已初始化: max_size={max_size}, max_idle={max_idle_time}s")

    def _start_cleanup_thread(self):
        """启动清理线程"""
        if self._cleanup_thread is None or not self._cleanup_thread.is_alive():
            self._cleanup_thread = threading.Thread(
                target=self._cleanup_loop,
                name="WebDriverCleanupThread",
                daemon=True
            )
            self._cleanup_thread.start()
            logger.info("WebDriver清理线程已启动")

    def _cleanup_loop(self):
        """清理循环"""
        cleanup_count = 0
        while not self._shutdown:
            try:
                time.sleep(WEBDRIVER_CLEANUP_INTERVAL)  # 使用配置常量
                cleanup_count += 1

                # 每WEBDRIVER_DEEP_CLEANUP_INTERVAL秒执行一次深度清理
                if cleanup_count % (WEBDRIVER_DEEP_CLEANUP_INTERVAL // WEBDRIVER_CLEANUP_INTERVAL) == 0:
                    self._deep_cleanup()

                # 清理过期和失效的driver
                self._cleanup_expired_drivers()

                # 监控内存
                self._monitor_memory()

            except Exception as e:
                logger.error(f"清理循环异常: {e}", exc_info=True)

    def _cleanup_expired_drivers(self):
        """清理过期和失效的WebDriver实例"""
        current_time = time.time()
        expired_infos = []

        with self._lock:
            for info in self._pool[:]:
                should_remove = False
                reason = ""

                # 检查过期
                if current_time - info.last_used > self.max_idle_time:
                    should_remove = True
                    reason = "过期"

                # 检查连续失败
                elif info.consecutive_failures >= self.max_consecutive_failures:
                    should_remove = True
                    reason = "连续失败过多"

                # 检查请求次数
                elif info.request_count >= self.max_request_count:
                    should_remove = True
                    reason = "请求次数过多"

                # 检查driver健康状态
                elif not self._is_driver_healthy(info.driver):
                    should_remove = True
                    reason = "健康检查失败"

                if should_remove:
                    expired_infos.append((info, reason))

            # 移除并清理
            for info, reason in expired_infos:
                self._remove_driver_info(info, reason)

    def _remove_driver_info(self, info: WebDriverInfo, reason: str):
        """移除driver信息并清理资源"""
        try:
            logger.info(f"移除WebDriver实例 (原因: {reason}, 请求次数: {info.request_count})")

            # 先从池中移除，避免重复使用
            if info in self._pool:
                self._pool.remove(info)

            # 关闭driver
            try:
                info.driver.quit()
                logger.debug("WebDriver已关闭")
            except Exception as e:
                logger.warning(f"关闭WebDriver失败: {e}")

            # 清理用户数据目录
            if info.user_data_dir:
                try:
                    if os.path.exists(info.user_data_dir):
                        shutil.rmtree(info.user_data_dir)
                        logger.debug(f"清理用户数据目录: {info.user_data_dir}")
                except Exception as e:
                    logger.warning(f"清理用户数据目录失败: {e}")

        except Exception as e:
            logger.error(f"移除driver信息时出错: {e}", exc_info=True)

    def _deep_cleanup(self):
        """深度清理：清理所有残留的Chrome相关进程和临时文件"""
        try:
            logger.info("执行深度清理...")

            # 清理Chrome进程
            kill_chrome_processes()

            # 清理 orphaned 临时目录
            temp_dir = tempfile.gettempdir()
            orphaned_count = 0
            for item in os.listdir(temp_dir):
                if item.startswith('chrome_profile_'):
                    item_path = os.path.join(temp_dir, item)
                    try:
                        # 检查是否还在使用中（通过检查锁文件等）
                        if not self._is_temp_dir_in_use(item_path):
                            shutil.rmtree(item_path)
                            orphaned_count += 1
                            logger.debug(f"清理orphaned目录: {item_path}")
                    except Exception as e:
                        logger.warning(f"清理目录失败 {item_path}: {e}")

            if orphaned_count > 0:
                logger.info(f"深度清理完成，清理了 {orphaned_count} 个orphaned目录")

            # 强制垃圾回收
            gc.collect()
            logger.debug("执行垃圾回收")

        except Exception as e:
            logger.error(f"深度清理失败: {e}", exc_info=True)

    def _is_temp_dir_in_use(self, temp_dir: str) -> bool:
        """检查临时目录是否正在使用中"""
        with self._lock:
            for info in self._pool:
                if info.user_data_dir == temp_dir:
                    return True
        return False

    def _monitor_memory(self):
        """监控内存使用情况"""
        try:
            process = psutil.Process()
            memory_info = process.memory_info()
            memory_mb = memory_info.rss / 1024 / 1024

            if memory_mb > self.memory_critical_threshold:
                logger.error(f"内存使用过高: {memory_mb:.2f}MB (阈值: {self.memory_critical_threshold}MB)，执行紧急清理")
                self._emergency_cleanup()

            elif memory_mb > self.memory_warning_threshold:
                logger.warning(f"内存使用较高: {memory_mb:.2f}MB (阈值: {self.memory_warning_threshold}MB)")
                # 清理一半的过期driver
                self._aggressive_cleanup()

        except Exception as e:
            logger.error(f"内存监控失败: {e}", exc_info=True)

    def _emergency_cleanup(self):
        """紧急清理：关闭所有非必要driver"""
        with self._lock:
            # 保留最新的一个，关闭其他的
            if len(self._pool) > 1:
                sorted_pool = sorted(self._pool, key=lambda x: x.last_used, reverse=True)
                keep_count = 1
                for info in sorted_pool[keep_count:]:
                    self._remove_driver_info(info, "紧急清理")

    def _aggressive_cleanup(self):
        """激进清理：清理所有过期driver"""
        current_time = time.time()
        with self._lock:
            expired = [info for info in self._pool
                      if current_time - info.last_used > self.max_idle_time / 2]

            for info in expired:
                self._remove_driver_info(info, "激进清理")

    def _is_driver_healthy(self, driver) -> bool:
        """分层健康检查"""
        try:
            # Level 1: 基本连接
            driver.current_url

            # Level 2: 窗口句柄
            driver.current_window_handle

            # Level 3: JavaScript执行
            driver.execute_script("return document.readyState;")

            # Level 4: 进程状态
            if hasattr(driver, 'service') and driver.service.process:
                if not driver.service.process.is_running():
                    return False

            return True

        except Exception as e:
            logger.debug(f"健康检查失败: {e}")
            return False

    def get_driver(self) -> Optional[object]:
        """获取WebDriver实例"""
        try:
            with self._lock:
                # 查找可用的健康driver
                for info in self._pool:
                    if self._is_driver_healthy(info.driver):
                        info.update_usage()
                        info.reset_failures()
                        return info.driver

                # 创建新的driver
                if len(self._pool) < self.max_size:
                    driver, user_data_dir = self._create_driver()
                    if driver:
                        info = WebDriverInfo(driver, user_data_dir)
                        self._pool.append(info)
                        logger.info(f"创建新WebDriver实例，当前池大小: {len(self._pool)}")
                        return driver

                logger.warning("WebDriver池已满，无法创建新实例")
                return None

        except Exception as e:
            logger.error(f"获取WebDriver失败: {e}", exc_info=True)
            return None

    def _create_driver(self) -> Tuple[Optional[object], Optional[str]]:
        """创建新的WebDriver实例"""
        from selenium.webdriver import Chrome, ChromeOptions
        from selenium.webdriver.chrome.service import Service as ChromeService
        import platform

        user_data_dir = None
        try:
            # 创建临时用户数据目录
            user_data_dir = self._resource_tracker.get_temp_dir('chrome_profile_')

            opt = ChromeOptions()
            opt.add_argument('--no-sandbox')
            opt.add_argument('--disable-setuid-sandbox')
            opt.add_argument('window-size=1920x3000')
            opt.add_argument('--disable-gpu')
            opt.add_argument('--disable-dev-shm-usage')
            opt.add_argument('--hide-scrollbars')
            opt.add_argument('blink-settings=imagesEnabled=false')
            opt.add_argument('--headless')
            opt.add_argument('--remote-debugging-port=0')
            opt.add_argument(f'--user-agent={get_random_user_agent()}')
            opt.add_argument(f'--user-data-dir={user_data_dir}')
            opt.add_argument('--disable-extensions')
            opt.add_argument('--disable-plugins')
            opt.add_argument('--disable-images')
            opt.add_argument('--disable-javascript')
            opt.add_argument('--disable-background-timer-throttling')
            opt.add_argument('--disable-backgrounding-occluded-windows')
            opt.add_argument('--disable-renderer-backgrounding')
            opt.add_argument('--disable-features=TranslateUI')
            opt.add_argument('--disable-ipc-flooding-protection')

            # 限制缓存大小
            opt.add_argument('--aggressive-cache-discard')
            opt.add_argument('--enable-aggressive-domstorage-flushing')

            # WSL环境配置
            if os.path.exists('/proc/version'):
                with open('/proc/version', 'r') as f:
                    if 'microsoft' in f.read().lower() or 'wsl' in f.read().lower():
                        opt.add_argument('--disable-dbus')
                        opt.add_argument('--disable-audio-output')
                        opt.add_argument('--disable-crash-reporter')
                        opt.add_argument('--disable-logging')
                        opt.add_argument('--log-level=3')
                        opt.add_argument('--silent')

            driver_path = None
            candidates = ['chromedriver', '/usr/local/bin/chromedriver', '/usr/bin/chromedriver']
            for p in candidates:
                path = shutil.which(p) if p == 'chromedriver' else (p if os.path.exists(p) else None)
                if path:
                    driver_path = path
                    break
            try:
                service = ChromeService(executable_path=driver_path) if driver_path else ChromeService()
            except TypeError:
                if not driver_path:
                    raise
                service = ChromeService(executable_path=driver_path)

            if platform.system() == 'Windows':
                try:
                    service.creation_flags = 0x08000000
                except Exception as e:
                    logger.warning(f"设置creation_flags失败: {e}")

            driver = Chrome(service=service, options=opt)
            driver.set_page_load_timeout(30)
            driver.implicitly_wait(10)

            logger.info(f"WebDriver创建成功: {user_data_dir}")
            return driver, user_data_dir

        except Exception as e:
            logger.error(f"创建WebDriver失败: {e}", exc_info=True)

            # 清理已创建的资源
            if user_data_dir and os.path.exists(user_data_dir):
                try:
                    shutil.rmtree(user_data_dir)
                    logger.debug(f"清理失败创建的目录: {user_data_dir}")
                except Exception as cleanup_error:
                    logger.warning(f"清理目录失败: {cleanup_error}")

            return None, None

    def return_driver(self, driver):
        """归还WebDriver实例到连接池"""
        try:
            with self._lock:
                # 查找对应的info
                for info in self._pool:
                    if info.driver == driver:
                        # Soft reset: 清理cookie和storage
                        self._soft_reset(driver)

                        # 更新使用统计
                        info.update_usage()

                        # 检查是否需要替换
                        if info.request_count >= self.max_request_count:
                            logger.info(f"请求次数已达上限 ({info.request_count})，替换driver")
                            self._remove_driver_info(info, "请求次数上限")
                        elif info.consecutive_failures >= self.max_consecutive_failures:
                            logger.warning(f"连续失败次数过多 ({info.consecutive_failures})，替换driver")
                            self._remove_driver_info(info, "连续失败过多")

                        return

            logger.warning("尝试归还不存在的driver")

        except Exception as e:
            logger.error(f"归还driver失败: {e}", exc_info=True)

    def _soft_reset(self, driver):
        """软重置：清理浏览器状态但不关闭"""
        try:
            # 清理cookies
            driver.delete_all_cookies()

            # 清理本地存储
            try:
                driver.execute_script("window.localStorage.clear();")
                driver.execute_script("window.sessionStorage.clear();")
            except Exception as e:
                logger.debug(f"清理存储失败: {e}")

            # 清理页面缓存
            try:
                driver.execute_script("window.caches && caches.keys().then(function(names) { names.forEach(function(name) { caches.delete(name); }); });")
            except Exception as e:
                logger.debug(f"清理缓存失败: {e}")

            # 导航到空白页
            try:
                driver.get("about:blank")
            except Exception as e:
                logger.debug(f"导航到空白页失败: {e}")

            logger.debug("软重置完成")

        except Exception as e:
            logger.warning(f"软重置失败: {e}")

    def close_all(self):
        """关闭所有WebDriver实例"""
        self._shutdown = True

        if self._cleanup_thread and self._cleanup_thread.is_alive():
            self._cleanup_thread.join(timeout=5)

        with self._lock:
            logger.info(f"关闭所有WebDriver实例 ({len(self._pool)} 个)")
            for info in self._pool[:]:
                self._remove_driver_info(info, "关闭所有")
            self._pool.clear()

        # 清理所有临时资源
        self._resource_tracker.cleanup_all()
            
# 全局WebDriver连接池实例
driver_pool = WebDriverPool()

# 进程监控和清理（改进版）
def kill_chrome_processes():
    """强制杀死所有Chrome相关进程"""
    killed_count = 0
    try:
        for proc in psutil.process_iter(['pid', 'name', 'cmdline']):
            try:
                # 检查进程名
                if proc.info['name'] and (
                    'chrome' in proc.info['name'].lower() or
                    'chromedriver' in proc.info['name'].lower()
                ):
                    cmdline = proc.info.get('cmdline', [])

                    # 检查是否是我们启动的进程（通过命令行参数判断）
                    is_our_process = False
                    if cmdline:
                        # 检查headless标志或我们的特定配置
                        if any(arg in cmdline for arg in ['--headless', '--user-data-dir']):
                            is_our_process = True

                    if is_our_process:
                        logger.info(f"终止Chrome进程: PID={proc.info['pid']}, Name={proc.info['name']}")
                        proc.terminate()
                        try:
                            proc.wait(timeout=CHROME_PROCESS_TIMEOUT)
                            killed_count += 1
                        except psutil.TimeoutExpired:
                            logger.warning(f"进程 {proc.info['pid']} 未响应SIGTERM，强制杀死")
                            proc.kill()
                            killed_count += 1

            except (psutil.NoSuchProcess, psutil.AccessDenied) as e:
                logger.debug(f"无法访问进程: {e}")
                continue

        if killed_count > 0:
            logger.info(f"成功终止 {killed_count} 个Chrome进程")

    except Exception as e:
        logger.error(f"清理Chrome进程时出错: {e}", exc_info=True)


# 程序退出时的清理函数
def cleanup_all_resources():
    """清理所有WebDriver资源和临时文件"""
    try:
        logger.info("开始清理所有资源...")

        # 关闭WebDriver池
        if driver_pool:
            driver_pool.close_all()

        # 等待进程正常关闭
        time.sleep(CHROME_PROCESS_TIMEOUT)

        # 强制清理残留进程
        kill_chrome_processes()

        # 强制垃圾回收
        gc.collect()

        logger.info("资源清理完成")

    except Exception as e:
        logger.error(f"清理资源时出错: {e}", exc_info=True)


# 注册程序退出时的清理函数
atexit.register(cleanup_all_resources)


@contextmanager
def get_webdriver():
    """改进的上下文管理器，提供更好的错误处理和资源管理"""
    driver = None
    driver_info = None
    try:
        # 获取driver
        driver = driver_pool.get_driver()
        if driver is None:
            raise RuntimeError("无法获取WebDriver实例，连接池已满或创建失败")

        yield driver

    except Exception as e:
        logger.error(f"WebDriver操作异常: {e}", exc_info=True)
        # 如果发生异常，标记driver为失效（通过连续失败计数）
        if driver:
            try:
                # 尝试标记driver为失效状态
                # 这里需要找到对应的info并增加失败计数
                # 由于contextmanager无法直接访问pool内部，这部分在pool.get_driver中处理
                pass
            except Exception as cleanup_error:
                logger.error(f"异常处理时清理driver失败: {cleanup_error}")
        raise

    finally:
        # 正常情况下归还driver到池中
        if driver:
            try:
                driver_pool.return_driver(driver)
            except Exception as e:
                logger.error(f"返回WebDriver到连接池失败: {e}", exc_info=True)
                # 如果归还失败，强制关闭driver
                try:
                    driver.quit()
                except Exception as quit_error:
                    logger.error(f"强制关闭WebDriver失败: {quit_error}", exc_info=True)


# 手机端User-Agent列表
MOBILE_USER_AGENTS = [
    "Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1",
    "Mozilla/5.0 (iPhone; CPU iPhone OS 14_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0 Mobile/15E148 Safari/604.1",
    "Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1",
    "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
    "Mozilla/5.0 (Linux; Android 10; SM-G981B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.162 Mobile Safari/537.36",
    "Mozilla/5.0 (Linux; Android 11; SM-G998B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.210 Mobile Safari/537.36",
    "Mozilla/5.0 (Linux; Android 12; SM-S908B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.127 Mobile Safari/537.36",
    "Mozilla/5.0 (Linux; Android 13; SM-S918B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.5481.153 Mobile Safari/537.36",
    "Mozilla/5.0 (iPad; CPU OS 13_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0 Mobile/15E148 Safari/604.1",
    "Mozilla/5.0 (iPad; CPU OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1"
]
import random
USER_AGENT = random.choice(MOBILE_USER_AGENTS)


def get_random_user_agent() -> str:
    """随机获取一个User-Agent"""
    return random.choice(MOBILE_USER_AGENTS)


def extract_url_from_text(text: str) -> Optional[str]:
    """从分享文本中提取抖音视频URL"""
    try:
        # 使用正则匹配URL
        url_pattern = r'https?://v.douyin.com/[\w\-\_/]+'
        match = re.search(url_pattern, text)
        logger.info(f"提取到链接：{match.group(0)}")
        return match.group(0) if match else None
    except Exception as e:
        logger.error(f"从分享文本提取URL失败: {e}")
        return None


def request_with_retry(url: str, headers: dict = None, max_retries: int = 3, interval: float = 0.2) -> Optional[requests.Response]:
    """带重试机制的请求函数"""
    for attempt in range(max_retries):
        try:
            response = requests.get(url, headers=headers, allow_redirects=False)
            response.raise_for_status()
            return response
        except requests.RequestException as e:
            logger.error(f"请求失败 (尝试 {attempt + 1}/{max_retries}): {e}")
            if attempt < max_retries - 1:
                time.sleep(interval)
    return None


def get_video_page(url: str) -> Optional[str]:
    """获取抖音视频页面HTML"""
    headers = {
        'User-Agent': USER_AGENT,
        'Referer': 'https://www.douyin.com/'
    }
    response = request_with_retry(url, headers=headers)
    return response.text if response else None

def get_videopage_fromselenium(url: str) -> Optional[str]:
    """使用Selenium获取抖音视频页面HTML"""
    max_retries = 3
    for attempt in range(max_retries):
        try:
            with get_webdriver() as driver:
                driver.get(url)
                logger.info(f"window_handle:{driver.current_window_handle}")
                html = driver.page_source
                return html
        except Exception as e:
            logger.error(f"尝试 {attempt + 1}/{max_retries} 失败: {e}")
            if attempt < max_retries - 1:
                time.sleep(1)  # 等待1秒后重试
            else:
                logger.error(f"所有尝试均失败，无法获取页面: {url}")
                return None
    return None

def extract_video_src(html: str) -> Optional[str]:
    """从HTML中提取video标签的src属性"""
    try:
        # 使用正则匹配video标签的src属性
        ret_url = None
        video_pattern = r'<video[^>]*src=["\']([^"\']+)["\']'
        match = re.search(video_pattern, html)
        if match:
            ret_url = match.group(1)
            ret_url = f"https://douyin.com{ret_url}"
            ret_url = ret_url.replace("&amp;", "&")
            ret_url = ret_url.replace("playwm", "play")
            logger.info(f"视频标签最终链接>>>>>{ret_url}<<<<<")
            #从 Url的查询参数中提取出video id参数，格式举例：https://douyin.com/aweme/v1/play/?line=0&logo_name=aweme_diversion_search&ratio=720p&video_id=v0300fg10000d46pv8nog65v91lcfc50
            video_id = None
            query_params = urlparse(ret_url).query
            if query_params:
                from urllib.parse import parse_qs
                params = parse_qs(query_params)
                video_id = params.get('video_id', [None])[0]
                if video_id:
                    ret_url = f"https://aweme.snssdk.com/aweme/v1/play/?video_id={video_id}"
        return ret_url
    except Exception as e:
        logger.error(f"提取视频链接失败: {e}")
        return None

import time
from selenium.webdriver import Chrome, ChromeOptions
from selenium.webdriver.chrome.service import Service as ChromeService
def get_lastredirect_url_fromchrome(url: str) -> Optional[str]:
    """使用Selenium获取最终重定向URL"""
    cookies = [
        {'name':'domain', 'value':'.douyin.com'},
        {'name':'path', 'value':'/'},
        {'name':'UIFID_TEMP','value':'4be83ecefa579a300714166db9e569bafd8689fc248d1e190e384db8df203b8172f4e975d73ef10f29adee786f36adac69ee89bff03bf6212a92479118b1db11f8161eb9cf0621140bec3f55c31b31f4'},
        {'name':'s_v_web_id','value':'verify_m9quqfjx_FEaKj3X1_pwnV_4uoL_8vjA_yQaBzbF9XeZz'},
        {'name':'hevc_supported','value': 'true'},
        {'name':'odin_tt','value':'e570e55059cafec9c0bb21dd3ecc30848f869c89234ed3eef4f1432baa5f5ec1ade5bb0a947dcb20b1bc23226ddcbee7562cbd6a57ef071683846a7ce02236c480e0a9d971fba1deb0ac73d1c1cabcde'},
        {'name':'fpk1','value':'U2FsdGVkX1+sWiZQTFfTl3PWK69/JFR9olAYjRUIM2jSSP4y/8qq9/vbxHoXXV4VytW7PCQ66H9YPZXUbv6Kdw=='},
        {'name':'fpk2','value':'33d0f257a817d1ca4c4381b87f8ad83f'},
        {'name':'passport_csrf_token','value':'a9d0eb9928896b2ec1c085d5acf65256'},
        {'name':'passport_csrf_token_default','value':'a9d0eb9928896b2ec1c085d5acf65256'},
        {'name':'UIFID','value':'4be83ecefa579a300714166db9e569bafd8689fc248d1e190e384db8df203b8172da0b3899e496c047a0f14d6470e59820f15ebebd509b2fca7c114d298da1ee702f8f4019787d72c23babbe758e9f9f33fdbdece86b3d8654bae41299c30936caac8e46ff57ae4ecbefe14c330ed3136dfb4c5860c6a13ee9e7e5f74b81be70b4b7778b1b3f40610c8e201b68e99691752535ac2d4c08ca89c1ac6e26837ae2'},
        {'name':'__security_mc_1_s_sdk_cert_key','value':'8b12a9c0-4dc6-9aa2'},
        {'name':'__security_mc_1_s_sdk_sign_data_key_web_protect','value':'6beb77fb-4fb7-af19'},
        {'name':'__security_mc_1_s_sdk_crypt_sdk','value':'2b25a342-4dcc-acec'},
        {'name':'bd_ticket_guard_client_web_domain','value':'2'},
        {'name':'__security_mc_1_s_sdk_sign_data_key_sso','value':'2193b783-4856-a6d9'},
        {'name':'xgplayer_user_id','value':'406940930752'},
        {'name':'my_rd','value':'2'},
        {'name':'ttwid','value':'1%7CYEdoR0QdySibDshx8gP8g0LBGmpBo7CGt6iAjHepXB4%7C1745637241%7Ce2589db372fed219c8f3f1f86af32f5b7dc1ac52376fc9172a677bea6ccad438'},
        {'name':'volume_info','value':'%7B%22isUserMute%22%3Afalse%2C%22isMute%22%3Atrue%2C%22volume%22%3A0.5%7D'},
        {'name':'xgplayer_device_id','value':'42882772649'},
        {'name':'is_dash_user','value':'1'},
        {'name':'FORCE_LOGIN','value':'%7B%22videoConsumedRemainSeconds%22%3A180%7D'},
        {'name':'strategyABtestKey','value':'%221746672019.89%22'},
        {'name':'__ac_signature','value':'_02B4Z6wo00f01rakbOAAAIDCgYCovNzYWI62hGhAAMXBe1'},
        {'name':'device_web_cpu_core','value':'16'},
        {'name':'device_web_memory_size','value':'8'},
        {'name':'architecture','value':'amd64'},
        {'name':'dy_swidth','value':'2560'},
        {'name':'dy_sheight','value':'1440'},
        {'name':'stream_recommend_feed_params','value':'%22%7B%5C%22cookie_enabled%5C%22%3Atrue%2C%5C%22screen_width%5C%22%3A2560%2C%5C%22screen_height%5C%22%3A1440%2C%5C%22browser_online%5C%22%3Atrue%2C%5C%22cpu_core_num%5C%22%3A16%2C%5C%22device_memory%5C%22%3A8%2C%5C%22downlink%5C%22%3A10%2C%5C%22effective_type%5C%22%3A%5C%224g%5C%22%2C%5C%22round_trip_time%5C%22%3A100%7D%22'},
        {'name':'home_can_add_dy_2_desktop','value':'%221%22'},
        {'name':'download_guide','value':'%222%2F20250508%2F0%22'},
        {'name':'bd_ticket_guard_client_data','value':'eyJiZC10aWNrZXQtZ3VhcmQtdmVyc2lvbiI6MiwiYmQtdGlja2V0LWd1YXJkLWl0ZXJhdGlvbi12ZXJzaW9uIjoxLCJiZC10aWNrZXQtZ3VhcmQtcmVlLXB1YmxpYy1rZXkiOiJCQnpKZ1pEY0NxdEJ4NlF2aXA5VVJCMDA2Si9wQXkrU0JSNGtDd0ZQWUZOTlE2OWMyV2huV1dBNDR3SytwcEhETTJMVlNuQTlFdnRWbm1hd1ZuSVJwQXc9IiwiYmQtdGlja2V0LWd1YXJkLXdlYi12ZXJzaW9uIjoyfQ%3D%3D'},
        {'name':'xg_device_score','value':'7.694897303211912'},
        {'name':'biz_trace_id','value':'0909ee89'},
        {'name':'IsDouyinActive','value': 'false'},
        {'name':'stream_player_status_params','value':'%22%7B%5C%22is_auto_play%5C%22%3A0%2C%5C%22is_full_screen%5C%22%3A0%2C%5C%22is_full_webscreen%5C%22%3A0%2C%5C%22is_mute%5C%22%3A1%2C%5C%22is_speed%5C%22%3A1%2C%5C%22is_visible%5C%22%3A0%7D%22'}
    ]
    
    max_retries = 3
    for attempt in range(max_retries):
        try:
            with get_webdriver() as driver:
                driver.get(url)
                for cookie in cookies:
                    try:
                        driver.add_cookie(cookie)
                    except Exception as e:
                        logger.warning(f"添加cookie失败: {e}")
                        
                driver.get(url)  # 再次访问，使cookie生效
                
                # 显式等待URL变化
                start_time = time.time()
                current_url = url
                while time.time() - start_time < 10:  # 最多等待10秒
                    try:
                        current_url = driver.current_url
                        if current_url and current_url.replace('www.', '') != url.replace('www.', ''):
                            break
                    except Exception as e:
                        logger.warning(f"获取当前URL失败: {e}")
                        break
                    time.sleep(0.5)
                
                current_url = current_url.replace('www.', '') if current_url else None
                if current_url and current_url != url.replace('www.', ''):
                    logger.info(f"最终真实链接：{current_url}")
                    return current_url
                else:
                    logger.error(f"没有找到重定向URL，可能是视频不存在或无法访问。")
                    return None
                    
        except Exception as e:
            logger.error(f"尝试 {attempt + 1}/{max_retries} 失败: {e}")
            if attempt < max_retries - 1:
                time.sleep(1)
            else:
                logger.error(f"所有尝试均失败，无法获取重定向URL: {url}")
                return None
    
    return None


def extract_redirect_url(response_text: str) -> Optional[str]:
    """从重定向页面提取视频URL"""
    try:
        redirect_pattern = r'<a href="([^"]+)">Found</a>'
        match = re.search(redirect_pattern, response_text)
        if not match:
            return None
        
        # 解析URL并去除参数部分
        parsed = urlparse(match.group(1))
        clean_url = f"{parsed.scheme}://{parsed.netloc}{parsed.path}"
        logger.info(f"提取到重定向URL：{clean_url}")
        return clean_url
    except Exception as e:
        logger.error(f"提取重定向URL失败: {e}")
        return None

#import json
def parse_douyin_url(share_text: str) -> Tuple[bool, Optional[str]]:
    """主函数：解析抖音分享链接获取无水印视频地址"""
    try:
        # 1. 从分享文本中提取URL
        url = extract_url_from_text(share_text)
        if not url:
            return False, "无法从文本中提取抖音视频URL"
            
        # 2. 获取第一次视频页面HTML
        html = get_video_page(url)
        if not html:
            return False, "无法获取第一次抖音视频页面"
        #logger.info(f"第一次HTML>>>>>{html[:1000]}<<<<<")
            
        # 3. 从重定向页面提取视频URL
        url = extract_redirect_url(html)
        if not url:
            return False, "无法从重定向页面提取视频URL"
        
        # 4. 重新获取视频页面HTML
        html = get_videopage_fromselenium(url)
        if not html:
            return False, "无法获取第二次抖音视频页面"
        """
        # TODO:提取出视频相关的额外信息=================
        pattern = r'window\._ROUTER_DATA\s*=\s*(.*?)</script>'
        match = re.search(pattern, html, re.DOTALL)
        if match:
            json_data = json.loads(match.group(1))
            with open("router.json", "w", encoding="utf-8") as f:
                json.dump(json_data, f, ensure_ascii=False, indent=4)
        """
        # 5. 提取video标签的src属性并拼接整理好完整url
        video_url = extract_video_src(html)
        if not video_url:
            return False, "无法从页面中提取视频链接"
        """
        # 6. 最后一步重定向
        final_url = get_lastredirect_url_fromchrome(video_url)
        if not final_url:
            return False, "无法获取最终重定向URL"
        """
        return True, video_url
    except Exception as e:
        return False, f"解析过程中发生错误: {str(e)}"